home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Text⁄Files / Tape Stuff / StandardGetFolder.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-11-29  |  20.7 KB  |  420 lines  |  [TEXT/KAHL]

  1. /*******************************************************************************
  2. * StandardGetFolder.c                                                          *
  3. *                                                                              *
  4. *    This little chunk o' code implements a way to let the user choose a       *
  5. *    folder to save files in via a StandardFile Dialog.                        *
  6. *                                                                              *
  7. *    Since the code uses the CustomGetFile function and depends on the use of  *
  8. *    FSSpec records, it only works under System 7.0 or later.                  *                        
  9. *                                                                              *
  10. *    And don't forget to include the custom dialog resources ( a 'DITL' and    *
  11. *   'DLOG') in your project.                                                   *                                                                   *
  12. *                                                                              *
  13. *    Portions of this code were originally provided by Paul Forrester          *
  14. *    (paulf@apple.com) to the think-c internet mailing list in response to my  *
  15. *    my question on how to do exactly what this code does.  I've added a       *
  16. *    couple of features, such as the ability to handle aliased folders and     *
  17. *    the programmer definable prompt.  I also cleaned and tightened the code,  *
  18. *    stomped a couple of bugs, and packaged it up neatly.  Bunches of work,    *
  19. *    but I learned A LOT about Standard File, the File Manager, the Dialog     *
  20. *    Manager, and the Alias Manager.  I tried to include in the comments some  *
  21. *    of the neat stuff I discovered in my hours of pouring over Inside Mac.    *
  22. *    Hope you find it educational as well as useful.                           *
  23. *******************************************************************************/
  24.  
  25. #include <stdio.h>
  26. #include <String.h>
  27. #include <Script.h>
  28. #include <Aliases.h>
  29.  
  30. /*=============================================================================+
  31. |                            Function Prototypes                               |
  32. +=============================================================================*/
  33.  
  34. void StandardGetFolder (Str255 message, StandardFileReply *mySFReply);
  35.  
  36. long GetSFCurDir(void);
  37.  
  38. short GetSFVRefNum(void);
  39.  
  40. pascal Boolean MyCustomGetDirectoryFileFilter(  CInfoPBPtr  myPB, 
  41.                                                 Ptr         myDataPtr);
  42.  
  43. void SetButtonTitle ( Handle    ButtonHdl, 
  44.                       Str255    name, 
  45.                       Rect      *ButtonRect);
  46.  
  47. pascal short MyCustomGetDirectoryDlogHook( short        item, 
  48.                                            DialogPtr    theDialog,
  49.                                            Ptr          myDataPtr);
  50.  
  51. char *StrCpy (char *s1, char *s2);
  52.  
  53. char *StrCat (char *s1, char *s2);                                      
  54.  
  55.  
  56. /*=============================================================================+
  57. |                               Resource IDs                                   |
  58. +=============================================================================*/
  59. #define rGetFolderButton            10
  60. #define rGetFolderMessage           11
  61. #define kFolderBit                  0x0010  
  62. #define rGetFolderDialog            2000
  63.  
  64.  
  65. /*=============================================================================+
  66. |                             Global Variables                                 |
  67. +=============================================================================*/
  68. static  char    gCurrentSelectedFolder [256];
  69.  
  70.  
  71. /*******************************************************************************
  72. * StandardGetFolder                                                            *
  73. *                                                                              *
  74. *     The StandardGetFolder function. You pass it the point where you want the *
  75. *     standard file dialog box drawn, the prompt to display above the file     *
  76. *     list, and a pointer to an StandardFileReply record.                      *
  77. *                                                                              *
  78. *     Upon return, the sfFile field of the SFReply record contains the volume  *
  79. *     reference number and directory ID that specify the folder the user       *
  80. *     chose. It also passes back the name of the chosen folder.  The sfGood    *
  81. *     field is set to true if the user chose a folder, or false if not.        *
  82. *******************************************************************************/
  83.  
  84. void StandardGetFolder (    Str255              message,
  85.                             StandardFileReply   *mySFReply)
  86.                             
  87. {
  88.     Point                   where = {-1, -1};
  89.     SFTypeList              theTypeList;
  90.     short                   numTypes;
  91.     ProcPtr                 myModalFilter;
  92.     CInfoPBRec              pb;
  93.     OSErr                   err;
  94.     short                   theItem;
  95.  
  96.     /*-------------------------------------------------------------------------+
  97.     | Setting num types to -1 tells CustomGetFile to pass all files and        |
  98.     | folders to the file filter function.                                     |
  99.     +-------------------------------------------------------------------------*/
  100.     numTypes = - 1;         
  101.  
  102.     /*-------------------------------------------------------------------------+
  103.     | Copy the prompt to be displayed above the file list into the name field  |
  104.     | of the SFReply record. When MyCustomGetDirectoryDlogHook is called for   |
  105.     | the first time, it will use this info to draw the prompt.                |
  106.     +-------------------------------------------------------------------------*/
  107.     StrCpy ( (char *) mySFReply->sfFile.name, (char *) message);
  108.     
  109.     /*-------------------------------------------------------------------------+
  110.     | Call CustomGetFile. Pass it a pointer to the file filter and dialog      |  
  111.     | hook functions. Also pass a pointer to mySFReply in the user data field. |
  112.     +-------------------------------------------------------------------------*/
  113.     CustomGetFile(  (ProcPtr)MyCustomGetDirectoryFileFilter, 
  114.                     numTypes,
  115.                     theTypeList,
  116.                     mySFReply, 
  117.                     rGetFolderDialog, 
  118.                     where,
  119.                     (ProcPtr)MyCustomGetDirectoryDlogHook, 
  120.                     NULL, 
  121.                     (short*)NULL,
  122.                     NULL, 
  123.                     (void *)(mySFReply));
  124.  
  125.     /*-------------------------------------------------------------------------+
  126.     | Ok, now the reply record contains the volume reference number and the    |
  127.     | name of the selected folder. We need to use PBGetCatInfo to get the      |    
  128.     | directory ID of the selected folder.                                     |
  129.     +-------------------------------------------------------------------------*/
  130.     pb.hFileInfo.ioCompletion = NULL;
  131.     pb.hFileInfo.ioNamePtr = mySFReply->sfFile.name;
  132.     pb.hFileInfo.ioVRefNum = mySFReply->sfFile.vRefNum;
  133.     pb.hFileInfo.ioFDirIndex = 0;
  134.     pb.hFileInfo.ioDirID = mySFReply->sfFile.parID;
  135.  
  136.     err = PBGetCatInfo( &pb, FALSE);
  137.     
  138.     /*-------------------------------------------------------------------------+
  139.     | Insert your error handler here. I couldn't think of one so I left it     |
  140.     | empty. Works fine without it.                                            |
  141.     +-------------------------------------------------------------------------*/
  142.     if (  err != noErr);
  143.     
  144.     /*-------------------------------------------------------------------------+
  145.     | Copy the directory ID of the selected folder to the sfFile field of the  |
  146.     | SFReply record.                                                          |
  147.     +-------------------------------------------------------------------------*/
  148.     mySFReply->sfFile.parID = pb.dirInfo.ioDrDirID;
  149.  
  150. }
  151.  
  152.  
  153.  
  154. /*******************************************************************************
  155. * MyCustomGetDirectoryFileFilter                                               *
  156. *                                                                              *
  157. *     This is the file filter passed to CustomGetFile. It passes folders only. *
  158. *******************************************************************************/
  159. pascal Boolean
  160. MyCustomGetDirectoryFileFilter( CInfoPBPtr  myPB, 
  161.                                 Ptr         myDataPtr )
  162. {
  163.     return( ! (myPB->hFileInfo.ioFlAttrib & kFolderBit ) );
  164. }
  165.  
  166.  
  167. /*******************************************************************************
  168. * MyCustomGetDirectoryDlogHook                                                 *
  169. *                                                                              *
  170. *     This function lets us process item hits in the GetFolderDialog.  We're   *
  171. *     only interested if the user hit the selectFolder button. We pass all     *
  172. *     other item hits back to ModalDialog.                                     *
  173. *******************************************************************************/
  174.  
  175. pascal short MyCustomGetDirectoryDlogHook(  short       item, 
  176.                                             DialogPtr   theDialog, 
  177.                                             Ptr         myDataPtr )
  178. {
  179.  
  180.     WindowPeek      dlgPeek;
  181.     Str255          selectedName;
  182.     CInfoPBRec      pb;
  183.     short           MyCustomGetDirectoryDlogHook;
  184.     OSErr           err;
  185.     int             itemType;           
  186.     Rect            itemRect;                                   
  187.     Handle          itemHandle;
  188.     Boolean         isAlias,
  189.                     isFolder;
  190.  
  191.     StandardFileReply           *mySFRPtr;
  192.  
  193.     
  194.     
  195.     /*-------------------------------------------------------------------------+
  196.     | Set the return value to defualt to the item that was passed in.          |
  197.     +-------------------------------------------------------------------------*/
  198.     MyCustomGetDirectoryDlogHook = item;
  199.  
  200.     /*-------------------------------------------------------------------------+
  201.     | CustomGet calls dialog hook for both main and subsidiary dialog boxes.   |
  202.     | Make sure that dialog record indicates that this is the main GetFolder   |
  203.     | dialog.                                                                  |
  204.     +-------------------------------------------------------------------------*/
  205.     dlgPeek = (WindowPeek)(theDialog);
  206.     if ( (OSType)(dlgPeek->refCon) == sfMainDialogRefCon )
  207.     {
  208.         /*---------------------------------------------------------------------+
  209.         | Get a handle to the select folder button, in case we need to change  |
  210.         | the label.                                                           |
  211.         +---------------------------------------------------------------------*/
  212.         GetDItem(theDialog, rGetFolderButton, &itemType, &itemHandle, &itemRect);
  213.  
  214.         /*---------------------------------------------------------------------+
  215.         | If this is the first time the dialog hook has been called...         |
  216.         +---------------------------------------------------------------------*/
  217.         if ( item == sfHookFirstCall )
  218.         {
  219.             /*-----------------------------------------------------------------+
  220.             | Set the prompt displayed above the file list...                  |
  221.             +-----------------------------------------------------------------*/
  222.             GetDItem ( theDialog, rGetFolderMessage, &itemType, &itemHandle, 
  223.                         &itemRect );
  224.             mySFRPtr = (StandardFileReply *)(myDataPtr);
  225.             SetIText ( itemHandle, mySFRPtr->sfFile.name );
  226.  
  227.             /*-----------------------------------------------------------------+
  228.             | And the name of the currently selected folder in the select      |
  229.             | folder button.                                                   |
  230.             +-----------------------------------------------------------------*/
  231.             pb.hFileInfo.ioCompletion = NULL;
  232.             pb.hFileInfo.ioNamePtr = (StringPtr)selectedName;
  233.             pb.hFileInfo.ioVRefNum = GetSFVRefNum();
  234.             pb.hFileInfo.ioDirID = GetSFCurDir();
  235.             pb.hFileInfo.ioFDirIndex = -1;
  236.             err = PBGetCatInfo( &pb, FALSE);
  237.  
  238.             /*-----------------------------------------------------------------+
  239.             | Note that this error return is important! When the dialog hook   |
  240.             | is called for the first time, Super Boomerang (and possibly      |
  241.             | Norton directory assistance aren't finished doing their          |
  242.             | rebounting, so the values returned by GetSFVRefNum and           |
  243.             | GetSFCurDir may not be valid, and hence PBGetCatInfo will return |
  244.             | an error.  That one took me a while to figure out.               |
  245.             +-----------------------------------------------------------------*/
  246.             if ( err != noErr )
  247.             {
  248.                 return (MyCustomGetDirectoryDlogHook);
  249.             }
  250.             
  251.             GetDItem(theDialog, rGetFolderButton, &itemType, &itemHandle, 
  252.                      &itemRect);
  253.             SetButtonTitle( itemHandle, selectedName, &itemRect);
  254.         }
  255.         
  256.         else
  257.         {
  258.             /*-----------------------------------------------------------------+
  259.             | Cast myDataPtr back to a SFReply pointer.                        |
  260.             +-----------------------------------------------------------------*/
  261.             mySFRPtr = (StandardFileReply *)( myDataPtr );
  262.  
  263.  
  264.             /*-----------------------------------------------------------------+
  265.             | If the selected folder is an alias, resolve it. isFolder will    |
  266.             | be set to true if a folder or aliased folder is selected.        |
  267.             +-----------------------------------------------------------------*/
  268.             ResolveAliasFile (&(mySFRPtr->sfFile), TRUE, &isFolder, &isAlias);
  269.             if ( (isAlias) && (isFolder) )
  270.                 StrCpy( (char *)selectedName, (char *)mySFRPtr->sfFile.name );
  271.                 
  272.             /*-----------------------------------------------------------------+
  273.             | If the selected item is a folder or volume, just copy the name   |
  274.             | into selectedName...                                             |
  275.             +-----------------------------------------------------------------*/
  276.             else if (( mySFRPtr->sfIsFolder) || (mySFRPtr->sfIsVolume) )
  277.                 StrCpy( (char *)selectedName, (char *)mySFRPtr->sfFile.name );
  278.  
  279.             /*-----------------------------------------------------------------+
  280.             | Otherwise, copy the name of the selected item's parent directory |
  281.             | into selectedName.                                               |
  282.             +-----------------------------------------------------------------*/
  283.             else
  284.             {
  285.                 pb.hFileInfo.ioCompletion = NULL;
  286.                 pb.hFileInfo.ioNamePtr = (StringPtr)selectedName;
  287.                 pb.hFileInfo.ioVRefNum = mySFRPtr->sfFile.vRefNum;
  288.                 pb.hFileInfo.ioDirID = mySFRPtr->sfFile.parID;
  289.                 pb.hFileInfo.ioFDirIndex = -1;
  290.                 err = PBGetCatInfo( &pb, FALSE);
  291.                 if ( err != noErr)
  292.                     return (MyCustomGetDirectoryDlogHook);
  293.             }
  294.             
  295.             /*-----------------------------------------------------------------+
  296.             | If the selected folder has changed since the last call to this   |
  297.             | dialog hook function, re-draw the button with the new selected   |
  298.             | folder name.                                                     |
  299.             +-----------------------------------------------------------------*/
  300.             if ( !EqualString( selectedName, (StringPtr)gCurrentSelectedFolder, 
  301.                                 FALSE, FALSE ) ) 
  302.                 SetButtonTitle(itemHandle, selectedName, &itemRect);
  303.  
  304.             /*-----------------------------------------------------------------+
  305.             | If the user clicked the select folder button, force a cancel and |
  306.             | set the sfGood field of the Reply record to true.                |
  307.             +-----------------------------------------------------------------*/
  308.             if (item == rGetFolderButton)
  309.             {
  310.                 MyCustomGetDirectoryDlogHook = sfItemCancelButton;
  311.                 mySFRPtr->sfGood = TRUE;
  312.             }
  313.                 
  314.         }
  315.     }
  316.     
  317.     return  (MyCustomGetDirectoryDlogHook );
  318. }
  319.  
  320.  
  321.  
  322. /*******************************************************************************
  323. * SetButtonTitle                                                               *
  324. *                                                                              *
  325. *     Whenever the selected folder is changed, SetButtonTitle is called to     *
  326. *     redraw the get folder button.  Pass it a handle to the button, the new   *
  327. *     string to be drawn in the button, and a pointer to the rect the button   *
  328. *     is drawn within.                                                         *
  329. *******************************************************************************/
  330. void SetButtonTitle(    Handle      ButtonHdl, 
  331.                         Str255      name, 
  332.                         Rect        *ButtonRect )
  333. {
  334.     short   resultCode;
  335.     short   width;
  336.     char    TmpStr[ 256 ];
  337.  
  338.     StrCpy( gCurrentSelectedFolder, (char*) name );
  339.     
  340.     /*-------------------------------------------------------------------------+
  341.     | Find the width left over in the button after drawing the word 'Select'   |
  342.     | the quotation marks. Truncate the new name to this length.               |
  343.     +-------------------------------------------------------------------------*/
  344.     width = (ButtonRect->right - ButtonRect->left) -
  345.             (StringWidth((StringPtr)"\pSelect \"\"") +
  346.              CharWidth('J'));
  347.     
  348.     resultCode = TruncString(width, name, smTruncEnd );
  349.     if ( resultCode < 0 );
  350.     
  351.     /*-------------------------------------------------------------------------+
  352.     | Redraw the button.                                                       |
  353.     +-------------------------------------------------------------------------*/
  354.     sprintf( TmpStr, "Select \"%#s\"", name );
  355.     CtoPstr( TmpStr );
  356.     SetCTitle((ControlHandle)(ButtonHdl), (StringPtr)TmpStr );
  357.     ValidRect(ButtonRect);
  358. }
  359.  
  360.  
  361. /*******************************************************************************
  362. * GetSFCurDir, GetSFVRefNum                                                    *
  363. *                                                                              *
  364. * The following set of routines are used to access a couple of low memory      *
  365. * globals that are necessary when extending Standard File.  One example is     *
  366. * trying to get the current directory while in a file filter.  These routines  *
  367. * were used to bottleneck all the low memory usage.  If the system one day     *
  368. * supports them with a trap call, then we can easily update these routines.    *
  369. *******************************************************************************/
  370.  
  371. long GetSFCurDir()
  372. {
  373.     return( *(long*)CurDirStore );
  374. }
  375.  
  376. short GetSFVRefNum()
  377. {
  378.     return( - *(short*)SFSaveDisk);
  379. }
  380.  
  381.  
  382. /*******************************************************************************
  383. * StrCpy                                                                       *
  384. *                                                                              *
  385. *     Just like strcpy except that it takes pascal strings as arguments.       *                                                                               *
  386. *******************************************************************************/
  387. char *StrCpy( char *s1, char *s2 )
  388. {
  389.     /*-------------------------------------------------------------------------+
  390.     | Copy The Length Byte                                                     |
  391.     +-------------------------------------------------------------------------*/
  392.     *s1 = *s2;
  393.  
  394.     /*-------------------------------------------------------------------------+
  395.     | Copy The Rest.                                                           |
  396.     +-------------------------------------------------------------------------*/
  397.     return( (char *)memcpy( s1+1, s2+1, *s1 ) );
  398. }
  399.  
  400.  
  401.  
  402. /*******************************************************************************
  403. * StrCat                                                                       *
  404. *                                                                              *
  405. *     Just like strcat except that it takes pascal strings as arguments.       *                                                                               *
  406. *******************************************************************************/
  407. char *StrCat( char *s1, char *s2 )
  408. {
  409.     int OriginalLen;
  410.  
  411.     OriginalLen = *s1;
  412.     *s1 += *s2;
  413.  
  414.     return( (char *)memcpy( s1+OriginalLen+1, s2+1, *s2 ) );
  415. }
  416.  
  417.  
  418.  
  419.  
  420.